using System;
using System.Collections.Generic;
using System.Text;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Factories;
using FarseerGames.GettingStarted.Demos.DemoShare;
using FarseerGames.GettingStarted.DrawingSystem;
using FarseerGames.GettingStarted.ScreenSystem;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Netpong.Networking;
using NetPong.Networking;
using NetPong.PongClasses;
using NetPong.PongClasses2d;
using XNALibrary;

namespace NetPong
{
    public class PongScreen : GameScreen
    {
        private Score score;
        private const int MAX_GAMERS = 31;
        private const int MAX_LOCAL_GAMERS = 1;
        private const int PADDLE_SPAWN_PADDING = 50;
        private NetworkManager networkManager;
        private RenderControl renderControl;

        private SpriteFont font;

        private Border _border;
        private TeamBorder teamBorder;
     
        //TODO: wrap in generic inputhandler
        KeyboardState currentKeyboardState;
        GamePadState currentGamePadState;

        List<ITransmittable> allGameObjs;
        List<IControllable> controllables;

        GameObjectFactory gameObjectFactory;

        public PongScreen(NetworkSession net)
            : base()
        {
            gameObjectFactory = new GameObjectFactory(_physicsSimulator, ScreenManager);
            networkManager = new NetworkManager(net, gameObjectFactory);
        }

        #region Initialize
        public override void Initialize()
        {
            //initially the game is paused
            Paused = true;
            ScreenManager.AddScreen(new PauseScreen(GetTitle(), GetDetails(), this));
            renderControl = new RenderControl(ScreenManager.game);

            //initialize game objects
            allGameObjs = new List<ITransmittable>();
            controllables = new List<IControllable>();
            
            score = new Score(networkManager);
            allGameObjs.Add(score);

            Vector2 ballPos = ScreenManager.ScreenCenter;
            ballPos.X += 75;
            Ball firstBall = new Ball2d(networkManager, ballPos, 25, _physicsSimulator, renderControl.ContentManager);
            firstBall.body.LinearVelocity = new Vector2(200, 200);
            allGameObjs.Add(firstBall);

            //Add network hooks
            if (networkManager.IsHost)
                networkManager.AddGamerJoinedHook(GamerJoinedEventHandler);
            
            networkManager.UpdateHostEvent += UpdateForHostEventHandler;
            networkManager.UpdateClientEvent += UpdateForClientEventHandler;

            base.Initialize();
        }

        void GamerJoinedEventHandler(object sender, GamerJoinedEventArgs e)
        {
            Vector2 newPaddlePosition = ScreenManager.ScreenCenter;
            if (networkManager.CountOfGamers % 2 == 1)
            {
                newPaddlePosition.X = PADDLE_SPAWN_PADDING;
                newPaddlePosition.Y = 100 + networkManager.CountOfGamers * 100;
            }
            else
            {
                newPaddlePosition.X = ScreenManager.ScreenWidth - PADDLE_SPAWN_PADDING;
                newPaddlePosition.Y = 100 + networkManager.CountOfGamers * 100;
            }

            // create the gamer's paddle
            // send a packet to the gamer with the paddle position and e.Gamer.Id
            // the client will create the paddle and associate with the Gamer's player index
            Paddle p = new Paddle(newPaddlePosition, PhysicsSimulator, ScreenManager, networkManager);
            allGameObjs.Add(p);
            controllables.Add(p);
            int netObjID = networkManager.CreateNetworkObj(p);
            networkManager.WriteToNetwork(p.WriteToNetwork(), e.Gamer);
        }

        public override void LoadContent()
        {
            int borderWidth = (int)(ScreenManager.ScreenHeight * .05f);
            _border = new Border(ScreenManager.ScreenWidth-5, ScreenManager.ScreenHeight-5, borderWidth,
                                 ScreenManager.ScreenCenter);
            
            _border.Load(ScreenManager.GraphicsDevice, PhysicsSimulator); //only loads the borders into the 

            teamBorder = new TeamBorder(ScreenManager.ScreenWidth/32, ScreenManager.ScreenHeight, borderWidth,
                                 ScreenManager.ScreenCenter);
            teamBorder.Load(ScreenManager.GraphicsDevice, PhysicsSimulator);
            
            font = ScreenManager.SpriteFonts.MenuSpriteFont;
        }
        #endregion

        #region Update

        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
                base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

                UpdateLocal(); //reads input for locally controlled objects
                
                // If we are in a network session, update it.
                networkManager.UpdateNetwork(gameTime);
        }

        public void UpdateForHostEventHandler(object sender, UpdateHostEventArgs args)
        {
            //the host maintains the updated physics
            this._physicsSimulator.Update(args.gameTime.ElapsedGameTime.Milliseconds * .001f);
        }

        public void UpdateForClientEventHandler(object sender, EventArgs e)
        {
            foreach (ITransmittable t in controllables)
            {
                networkManager.WriteToNetwork(t.WriteToNetwork(), networkManager.Host);
            }
        }

        //TODO
        public void PacketReceivedEventHanderForClient()
        {

        }

        /// <summary>
        /// updates locally controlled objects.
        /// </summary>
        void UpdateLocal()
        {
            foreach (IControllable c in controllables)
                c.ReadInput();
        }

        #endregion

        #region Handle Input        

        /// <summary>
        /// Checks if the specified button is pressed on either keyboard or gamepad.
        /// </summary>
        bool IsPressed(Keys key, Buttons button)
        {
            return (currentKeyboardState.IsKeyDown(key) ||
                    currentGamePadState.IsButtonDown(button));
        }

        #endregion

        public override void Draw(GameTime gameTime)
        {
            drawBackground();

            teamBorder.Draw(ScreenManager.SpriteBatch);
            
            DrawNetworkSession(gameTime);

            base.Draw(gameTime);
        }

        /// <summary>
        /// Draws the state of an active network session.
        /// </summary>
        void DrawNetworkSession(GameTime gameTime)
        {
            foreach (ITransmittable body in allGameObjs)
            {
               // body.Render(gameTime);
            }

            ScreenManager.SpriteBatch.Begin();
            string scoreX = score.score.X.ToString();//RIGHT TEAM
            string scoreY = score.score.Y.ToString();//LEFT TEAM
            Vector2 xPos = Vector2.Zero;
            xPos.X += 10;
            xPos.Y += 10;
            Vector2 yPos = Vector2.Zero;
            yPos.X = ScreenManager.ScreenWidth - 35;
            yPos.Y += 10;
            ScreenManager.SpriteBatch.DrawString(font, scoreX, yPos, Color.White);
            ScreenManager.SpriteBatch.DrawString(font, scoreY, xPos, Color.White);
            ScreenManager.SpriteBatch.End();
        }

        public void drawBackground()
        {
            Texture2D blankText; 
            Body centerRect = BodyFactory.Instance.CreateRectangleBody(15f, ScreenManager.ScreenHeight, 1f);
            //Body leftRect = BodyFactory.Instance.CreateRectangleBody(15f, ScreenManager.ScreenHeight, 1f);
            //Body rightRect = BodyFactory.Instance.CreateRectangleBody(15f, ScreenManager.ScreenHeight, 1f);
            
            ScreenManager.SpriteBatch.Begin();
            blankText = DrawingHelper.CreateCircleTexture(ScreenManager.GraphicsDevice, 100, Color.White);
            centerRect.Position = new Vector2(ScreenManager.ScreenWidth / 2 - 95, ScreenManager.ScreenHeight / 2 - 100);
            ScreenManager.SpriteBatch.Draw(blankText, centerRect.Position, Color.White);

            blankText = DrawingHelper.CreateCircleTexture(ScreenManager.GraphicsDevice, 90, Color.White);
            centerRect.Position = new Vector2(ScreenManager.ScreenWidth / 2 - 85, ScreenManager.ScreenHeight / 2 - 90);
            ScreenManager.SpriteBatch.Draw(blankText, centerRect.Position, Color.CornflowerBlue);

            blankText = DrawingHelper.CreateCircleTexture(ScreenManager.GraphicsDevice, 30, Color.White);
            centerRect.Position = new Vector2(ScreenManager.ScreenWidth / 2 - 25, ScreenManager.ScreenHeight / 2 - 30);
            ScreenManager.SpriteBatch.Draw(blankText, centerRect.Position, Color.White);

            centerRect.Position = new Vector2((ScreenManager.ScreenWidth / 2) , 0);
            blankText = DrawingHelper.CreateRectangleTexture(ScreenManager.GraphicsDevice, 15, ScreenManager.ScreenHeight, Color.White);
            ScreenManager.SpriteBatch.Draw(blankText, centerRect.Position, Color.White);
            blankText = DrawingHelper.CreateRectangleTexture(ScreenManager.GraphicsDevice, 5, ScreenManager.ScreenHeight, Color.White);
            centerRect.Position = new Vector2((ScreenManager.ScreenWidth / 2) - 25, 0);
            ScreenManager.SpriteBatch.Draw(blankText, centerRect.Position, Color.White);
            centerRect.Position = new Vector2((ScreenManager.ScreenWidth / 2) + 35, 0);
            ScreenManager.SpriteBatch.Draw(blankText, centerRect.Position, Color.White);

            ScreenManager.SpriteBatch.End();
        }

        public override void HandleInput(InputState input)
        {
            if (input.PauseGame)
            {
                ScreenManager.AddScreen(new PauseScreen(GetTitle(), GetDetails(), this));
                Paused = true;
            }

            base.HandleInput(input);
        }

        public static string GetTitle()
        {
            return "Network Pong";
        }

        public static string GetDetails()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("Play Pong with your friends over the network!\n\n");
            sb.AppendLine("Keyboard Controls:");
            sb.AppendLine("  -Rotate : left and right arrows");
            sb.AppendLine("  -Move : A,S,D,W");
            return sb.ToString();
        }
    }
}